package com.qcby.Service.ServiceImpl;

import com.qcby.Entity.*;
import com.qcby.Mapper.*;
import com.qcby.Service.TrainDetailService;
import com.qcby.Service.TrainNumService;
import com.qcby.query.CityQuery;
import com.qcby.query.TrainNumQuery;
import com.qcby.query.TrainStationQuery;
import com.qcby.util.DeepCopyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.LongStream;

@Service
public class TrainNumServiceImpl implements TrainNumService {

    @Autowired
    private TrainNumMapper trainNumMapper;

    @Autowired
    private TrainDetailMapper trainDetailMapper;

    @Autowired
    private TrainOrderMapper trainOrderMapper;
    @Autowired
    private TrainStationMapper trainStationMapper;
    @Autowired
    private CityMapper cityMapper;

    public List<TrainNumQuery> getTrainWithDetails(String time){
        return trainNumMapper.getTrainWithDetails(time);
    }
    public TrainNumQuery getTrainWithDetailsByTrainId(int id)
    {
        return  trainNumMapper.getTrainWithDetailsByTrainId(id);
    }

    /*
    * @Transactional
    * 该注解可以让Spring会自动为你开启和关闭事务
    * 比AOP手动去控制事务要方便且不容易出错
    * */
    @Transactional
    public void addTrainWithDetail(TrainNumQuery trainNumQuery){
        //开启事务
        //在列次表里添加一条记录
        TrainNum trainNum=new TrainNum();
        trainNum.setIndex(0);
        trainNum.setCarriageNum(trainNumQuery.getCarriageNum());
        trainNum.setName(trainNumQuery.getName());
        trainNum.setTime(trainNumQuery.getTime());
        trainNum.setStatus(0);
        byte[] seat = new byte[trainNumQuery.getCarriageNum()*30*trainNumQuery.getTrainDetailQueries().size()];
        trainNum.setSeat(seat);
        trainNumMapper.insertTrain(trainNum);
        Date time = trainNumQuery.getTime();
        //System.out.println("刚插入的列次信息主键id为:"+trainNum.getId());
        //返回刚插入的列次的id
        //遍历列次详情,在列次详情表里添加记录
        for(int i=0;i<trainNumQuery.getTrainDetailQueries().size();i++){
            TrainDetail trainDetail=new TrainDetail();
            trainDetail.setGoneTime(time);
            trainDetail.setFromStationId(trainNumQuery.getTrainDetailQueries().get(i).getFromStationId());
            trainDetail.setToStationId(trainNumQuery.getTrainDetailQueries().get(i).getToStationId());
            trainDetail.setTrainNumId(trainNum.getId());
            trainDetail.setMoney(trainNumQuery.getTrainDetailQueries().get(i).getMoney());
            trainDetail.setRunMinute(trainNumQuery.getTrainDetailQueries().get(i).getRunMinute());
            trainDetail.setStationIndex(trainNumQuery.getTrainDetailQueries().get(i).getStationIndex());
            String ticket = "-"+30*trainNumQuery.getCarriageNum();
            String tic = "" + 30 * trainNumQuery.getCarriageNum();
            for(int a=i+1;a<trainNumQuery.getTrainDetailQueries().size();a++){
                tic = tic + ticket;
            }
            trainDetail.setTicket(tic);
            if(i<trainNumQuery.getTrainDetailQueries().size()-1){
                trainDetail.setWaitMinute(trainNumQuery.getTrainDetailQueries().get(i).getWaitMinute());
            }else{
                trainDetail.setWaitMinute(0);
            }
            //上传
            trainDetailMapper.insertTrainDetail(trainDetail);
            time = addMinutes(time,trainDetail.getWaitMinute());
            time = addMinutes(time,trainDetail.getRunMinute());
        }
        //关闭事务
    }

    @Transactional
    public void deleteTrainWithDetailById(Integer id){
        //删除该列次
        //删除该列次相关的所有的列次明细
        //开启事务
        trainNumMapper.deleteById(id);
        trainDetailMapper.deleteById(id);
        //关闭事务
    }

    @Transactional
    public List<TrainNumQuery> SearchTrainWithDetailsAndOrders(Integer fromStationId,Integer toStationId,String date){
        //1.将传入的yyyy-MM-dd格式的date转换为Date对象
        //2.根据date找出日期与目标日期一致的 列次 - 列次详情 - 订单
        //3.对每一个结果判断列次是否途径始发站和终点站
        //4.获取对应列次的响应余票数

        //返回的数据
        List<TrainNumQuery> result = new ArrayList<TrainNumQuery>();

        //1.将传入的yyyy-MM-dd格式的date转换为Date对象
        // 定义日期格式
        Date time;
        String dateFormat = "yyyy-MM-dd";
        // 创建SimpleDateFormat对象并设置格式
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        // 将字符串解析为Date对象
        try {
            time = sdf.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        //2.根据date找出日期与目标日期一致的 列次 - 列次详情 - 订单
        List<TrainNumQuery> trainNumQueryList = trainNumMapper.getTrainWithDetailsAndOrders(time);

        //3.对每一个结果判断列次是否途径始发站和终点站
        //遍历当天的所有列次
        for(int i = 0; i < trainNumQueryList.size(); i++){
            //获取当前列次一共有多少段 段数 = 站点数 -1
            int stationNum = trainNumQueryList.get(i).getTrainDetailQueries().size();
            //fromIndex 为从第几站上车 toIndex是从第几站下车
            int fromIndex = 0;
            int toIndex = 0;
            if(fromStationId == trainNumQueryList.get(i).getTrainDetailQueries().get(0).getFromStationId()){
                fromIndex = 1;
            }
            for(int a = 0; a<stationNum; a++){
                if(fromIndex == 0){
                    if(fromStationId == trainNumQueryList.get(i).getTrainDetailQueries().get(a).getToStationId()){
                        fromIndex = a+2;
                    }
                }else if(toIndex == 0){
                    if(toStationId == trainNumQueryList.get(i).getTrainDetailQueries().get(a).getToStationId()){
                        toIndex = a+2;
                    }
                }
            }
            //如果 fromIndex 或者 toIndex 有一个为0 就是没有通过这两站 就不再继续执行
            if( fromIndex!=0 && toIndex!=0 ){
                //记录是从第几站上车,走了多少站,价格是多少,时间是多少,上车时间是什么时候,下车时间是什么时候,余票是多少
                //从第几站上车
                trainNumQueryList.get(i).setFromIndex(fromIndex);
                //从第几站下车
                trainNumQueryList.get(i).setToIndex(toIndex);
                //价格
                int price=0;
                for(int a=fromIndex;a<toIndex;a++){
                    //数据库存储的价格为Varchar 转换为int
                    int number=0;
                    try {
                        number = Integer.parseInt(trainNumQueryList.get(i).getTrainDetailQueries().get(a-1).getMoney());
                    } catch (NumberFormatException ignored) {
                    }
                    price+=number;
                }
                trainNumQueryList.get(i).setPrice(price);
                //上车时间
                Date fromTime = trainNumQueryList.get(i).getTime();
                int minutes = 0;
                for(int a=0;a<fromIndex-1;a++){
                    minutes += trainNumQueryList.get(i).getTrainDetailQueries().get(a).getRunMinute();
                    minutes += trainNumQueryList.get(i).getTrainDetailQueries().get(a).getWaitMinute();
                }
                fromTime =addMinutes(fromTime,minutes);
                trainNumQueryList.get(i).setFromTime(fromTime);
                //下车时间
                minutes = 0;
                for(int a=fromIndex-1;a<toIndex-2;a++){
                    minutes += trainNumQueryList.get(i).getTrainDetailQueries().get(a).getRunMinute();
                    minutes += trainNumQueryList.get(i).getTrainDetailQueries().get(a).getWaitMinute();
                }

                minutes += trainNumQueryList.get(i).getTrainDetailQueries().get(toIndex-2).getRunMinute();
                trainNumQueryList.get(i).setToTime(addMinutes(fromTime,minutes));
                //余票数量
                String tickets = trainNumQueryList.get(i).getTrainDetailQueries().get(fromIndex-1).getTicket();
                String[] parts = tickets.split("-");
                List<Integer> ticketsList = new ArrayList<>();
                for(String part : parts){
                    ticketsList.add(Integer.parseInt(part));
                }
                //System.out.println(ticketsList.toString());
                trainNumQueryList.get(i).setTicket(ticketsList.get(toIndex-fromIndex-1));
                //数据整理好加入到result中
                result.add(trainNumQueryList.get(i));
            }
        }
        return result;
    }

    //获取一个Date对象minutes分钟之后的时间
    public static Date addMinutes(Date date, int minutes) {
        // 获取一个日历实例
        Calendar calendar = Calendar.getInstance();
        // 将给定的Date对象设置为日历的当前时间
        calendar.setTime(date);
        // 在日历上加上指定的分钟数
        calendar.add(Calendar.MINUTE, minutes);
        // 返回修改后的时间（Date类型）
        return calendar.getTime();
    }
    //将Date对象转换为yyyy-MM-dd类型的字符串
    public static String transDatetoString(Date date){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 使用SimpleDateFormat对象的format方法将Date对象转换为字符串
        String formattedDate = sdf.format(date);
        return formattedDate;
    }
    @Transactional
    public TrainOrder buyTrainTicket(TrainOrder trainOrder){
        //传来的参数:trainNumId fromStationId toStationId fromStationIndex toStationIndex travellerId userId
        //1.分配座位 -> 获取当前是该在哪个车厢放票了,获取该车厢的订单信息,比对应放在哪个座位,如果没有座位则换下一个车厢
        //2.修改余票 -> 判断有无与此票同用一个座位的票,如果有这几张票合算减余票数量
        //3.添加订单 -> 添加


        //1.分配座位
        //首先先获取列次信息,根据当前Index是几来判断分到第几车厢
        TrainNumQuery trainNum =trainNumMapper.getTrainWithDetailsByTrainId(trainOrder.getTrainNumId());
        int index = trainNum.getIndex();
        //@Params:carriageNum 该车次的车厢数总数
        int carriageNum = trainNum.getCarriageNum();
        //车厢号从1开始数 @Params:toWhichCarriage 存在哪个车厢
        int toWhichCarriage = index%carriageNum+1;
        //@Params:detailNum 该车厢的段数
        int detailNum = trainNum.getTrainDetailQueries().size();
        //将始发站id和末站id赋值给trainOrder
        trainOrder.setFromStationId(trainNum.getTrainDetailQueries().get(trainOrder.getFromStationIndex()-1).getFromStationId());
        trainOrder.setToStationId(trainNum.getTrainDetailQueries().get(trainOrder.getToStationIndex()-2).getToStationId());
        //@Params:seatNum 分配到那一个座位
        int seatNum = 0;
        for(int x=0;x<detailNum&&seatNum==0;x++){
            if(x !=0){
                //如果这个车厢没有位置,去下一个车厢进行查询
                index++;
            }
            trainNumMapper.addIndex(index+1, trainNum.getId());
            // @Params:seat 存储座位状况的比特流
            byte[] seat =trainNum.getSeat();
            // 从第几站出发,到第几站去,走了几段
            int startNum = trainOrder.getFromStationIndex();
            int endNum = trainOrder.getToStationIndex();
            //int length = endNum-startNum;
            // 对这个车厢的比特流进行遍历,每次获取一个座位的比特流,对比当前座位的比特流是否能容得下这张票
            for(int i=(index%carriageNum)*30*detailNum;i<(index%carriageNum+1)*30*detailNum;i+=detailNum){
                //记录这个座位能不能放下
                int whetherLogic =0;
                for(int j=startNum;j<endNum;j++){
                    if(seat[i+j-1] != 0){
                        //不合法
                        System.out.println(seat[i+j-1]);
                        whetherLogic++;
                    }
                }
                if(whetherLogic==0){
                    //在此落座
                    seatNum=(i-(index%carriageNum)*30*detailNum)/detailNum+1;
                    System.out.println("发配到"+(index%carriageNum+1)+"车厢");
                    //修改比特流中的数据
                    for(int j=startNum;j<endNum;j++){
                       seat[i+j-1] = 1;
                    }
                    //将修改后的比特流传回给数据库
                    trainNumMapper.updateSeat(seat, trainNum.getId());
                    toWhichCarriage = index%carriageNum+1;
                    //结束循环
                    i=carriageNum*30*detailNum;
                }
            }
        }
        //得到的座位号给trainOrder
        trainOrder.setSeatNum(seatNum);
        trainOrder.setCarriageNum(toWhichCarriage);
        System.out.println("座位号:"+seatNum+" 车厢号:"+toWhichCarriage);


        //2.修改余票
        //获取有没有和该座位共用一个座位的票
        List<TrainOrder> orderList = trainOrderMapper.getSameSeatOrder(trainNum.getId(),toWhichCarriage,seatNum);
        //System.out.println(trainNum.getId()+" "+toWhichCarriage+" "+seatNum);
        //n段的列次 卖出票的方法有 n + (n-1) + (n-2) + (n-3) + …… + 2 + 1 种 即 (n+1)*n/2 种
        //用比特流来记录一下在买票之前该座位被占状态 1空0被占用
        byte[] stationsOccupied = new byte[detailNum];
        for (TrainOrder order : orderList) {
            int startNum = order.getFromStationIndex();
            int endNum = order.getToStationIndex();
            for(int i = startNum;i<endNum ;i++){
                stationsOccupied[i-1] =1;
            }
        }
        System.out.println("before占用情况"+Arrays.toString(stationsOccupied));
        //@Params:ticketBeforeBuy 记录买票之前该座位的余票
        byte[] ticketsBeforeBuy = new byte[(detailNum+1)*detailNum/2];
        int count = 0;
        for(int i=0;i<detailNum;i++){
            for(int j=i;j<detailNum;j++){
                if(stationsOccupied[j]==0){
                    ticketsBeforeBuy[count] = 1;
                    count++;
                }else{
                    //遇到已被占用的坐直接跳过后面所有
                    count += (detailNum-j);
                    j=detailNum;
                }
            }
        }
        //记录买票之后该座位的被占情况
        for(int i = trainOrder.getFromStationIndex();i<trainOrder.getToStationIndex();i++){
            stationsOccupied[i-1] = 1;
        }
        System.out.println("after占用情况"+Arrays.toString(stationsOccupied));
        //@Params:ticketAfterBuy 记录买票之后该座位的余票
        byte[] ticketsAfterBuy = new byte[(detailNum+1)*detailNum/2];
        count = 0;
        for(int i=0;i<detailNum;i++){
            for(int j = i;j<detailNum;j++){
                if(stationsOccupied[j]==0){
                    ticketsAfterBuy[count] = 1;
                    count++;
                }else{
                    //遇到已被占用的坐直接跳过后面所有 节省时间复杂度
                    count += (detailNum-j);
                    j=detailNum;
                }
            }
        }
        //计算买票前后余票变化 即找 before 中为1 且 after 中为0 的
        //变化只需要看前后不同的就行,只能由 1 -> 0
        System.out.println("before余票情况"+Arrays.toString(ticketsBeforeBuy));
        System.out.println("after余票情况"+Arrays.toString(ticketsAfterBuy));
        List<List<Integer>> ticketList = new ArrayList<List<Integer>>();
        List<String> ticketStr = new ArrayList<>();
        //获取之前的余票信息
        //将余票信息有变动的地方减一
        //存储到一个List里
        //@count 记录当前读取票数的位置
        int count1 = 0;
        for(int i = 0;i<trainNum.getTrainDetailQueries().size();i++){
            //将取回的 xxx-xxx-xxx格式的字符串转换为 String数组
            String[] parts = trainNum.getTrainDetailQueries().get(i).getTicket().split("-");
            List<Integer> tickets = new ArrayList<Integer>();
            for(String part:parts){
                if(ticketsBeforeBuy[count1]!=ticketsAfterBuy[count1]){
                    tickets.add(Integer.parseInt((part))-1);
                }else{
                    tickets.add(Integer.parseInt(part));
                }
                count1++;
            }
            ticketList.add(tickets);
            //将买后余票数List转换为xxx-xxx-xxx的字符串
            //将其上传回数据库
            String str ="";
            for(int j=0;j<tickets.size();j++){
                if(j!=tickets.size()-1){
                    str += tickets.get(j)+"-";
                }else{
                    str += tickets.get(j);
                }
            }
            ticketStr.add(str);
        }
        System.out.println("具体余票详情"+ticketList);
        //将新的余票数上传数据库
        for(int i=0;i<ticketStr.size();i++){
            trainDetailMapper.updateTicketNum(ticketStr.get(i),trainOrder.getTrainNumId(),i);
        }
        //3.添加订单
        //trainNumId自带,fromStationId自带,toStationId自带,fromStationIndex自带,toStationIndex自带,
        //startTime自带,endTime自带,carriageNum,seatNum,travellerId自带,userId自带,status为零
        trainOrderMapper.insertOrder(trainOrder);
        return trainOrder;
    }

    @Transactional
    public List<List<TrainNumQuery>> SearchTransTrainWithDetails(Integer fromStationId, Integer toStationId, String date) {
        //@Params:date yyyy-MM-dd 格式的字符串
        //查询换乘的车票
        //1.获取始发站和终点站的城市坐标
        //2.计算两城市坐标的中点(圆心)以及两城市之间的距离(直径)
        //3.去数据库中查询坐标距离圆心小于半径的城市,顺带用collection将在该城市的车站查询出来(可作为中转站的站点)
        //4.遍历中转站查询 始发站->中转站->终点站 且到中转站时间早于从中转站出发时间 且中间等待时间不超过maxWaitTime小时 且前后都有余票的列次
        //先判断始发站到中转站有没有可行列次，没有直接看下一个中转站
        //有的话每有一个从始发站到终点站的列次都根据前半段到达时间maxWaitTime小时内为出发时间查询前往终点站的列次

        //最大等待时间不写死
        int maxWaitTime = 8;
        //结果集
        List<List<TrainNumQuery>> result = new ArrayList<List<TrainNumQuery>>();
        //1.获取始发站和终点站的城市坐标
        TrainStationQuery fromStation = trainStationMapper.getStationWithCity(fromStationId);
        TrainStationQuery toStation = trainStationMapper.getStationWithCity(toStationId);
        //始发站的 经纬度
        double fromLatitude = fromStation.getCity().getLatitude();
        double fromLongitude = fromStation.getCity().getLongitude();
        //终点站的 经纬度
        double toLatitude = toStation.getCity().getLongitude();
        double toLongitude = toStation.getCity().getLongitude();
        //2.计算
        //圆心纬度 *2
        double latitude = fromLatitude+toLatitude;
        //圆心经度 *2
        double longitude = fromLongitude+toLongitude;
        //直径 平方
        double radius = (fromLatitude-toLatitude)*(fromLatitude-toLatitude)+(fromLongitude-toLongitude)*(fromLongitude-toLongitude);
        //3.数据库中查询符合条件的中转站
        List<CityQuery> cityQueryList = cityMapper.getTransCityWithStations(longitude,latitude,radius);
        //System.out.println(cityQueryList.size());
        //4.遍历中转站查询 始发站->中转站->终点站 且到中转站时间早于从中转站出发时间 且中间等待时间不超过 maxWaitTime 小时 且前后都有余票的列次
        for(CityQuery cityQuery:cityQueryList){
            //先判断从始发站到中转站有无可行列次
            for(TrainStation trainStation:cityQuery.getStations()){
                //查询始发站到中转站的列次
                //@Params:trainQueryList =》 Date toTime到达时间 Integer price价格 Integer Ticket余票数
                /*List<TrainNumQuery> trainNumQueryList = new ArrayList<TrainNumQuery>();*/
                //System.out.println("中转站："+trainStation.getId());
                //System.out.println("出发站："+fromStationId);
                //System.out.println(date);
                List<TrainNumQuery> trainNumQueryList = SearchTrainWithDetailsAndOrders(fromStationId,trainStation.getId(),date);
                for(int i=0;i<trainNumQueryList.size();i++){
                    //判断从始发站到该中转站的该列次是否有余票
                    if(trainNumQueryList.get(i).getTicket()!=0){
                        //如果有余票就查找最近 maxWaitTime 小时内从中转站到终点站的列次
                        //抵达中转站的时间
                        Date arriveTime = trainNumQueryList.get(i).getToTime();
                        //下一趟车的最晚出发时间 arriveTime + maxWaitTime
                        Date latestGoneTime = addMinutes(arriveTime,maxWaitTime*60);
                        //获取符合条件的中转后的列次
                        List<TrainNumQuery> trainNumQueryList1 = SearchTrainWithDetailsAndOrders(trainStation.getId(),toStationId,transDatetoString(arriveTime));
                        List<TrainNumQuery> trainNumQueryList2 = SearchTrainWithDetailsAndOrders(trainStation.getId(),toStationId,transDatetoString(addMinutes(arriveTime,24*60)));
                        trainNumQueryList1.addAll(trainNumQueryList2);
                        //循环遍历获取的可行列次
                        for(int j=0;j<trainNumQueryList1.size();j++){
                            if(trainNumQueryList1.get(j).getToTime().after(arriveTime)&&trainNumQueryList1.get(j).getToTime().before(latestGoneTime)){
                             //装入result
                                List<TrainNumQuery> trainNumQueries = new ArrayList<>();
                                //trainNumQueries.add(trainNumQueryList.get(i));
                                //trainNumQueries.add(trainNumQueryList1.get(j));
                                //有个bug因为循环会修改TrainNumQuery的值
                                //导致result中存储的地址不变，地址存的值会变
                                //因此这里选择使用深拷贝
                                trainNumQueries.add(DeepCopyUtil.deepCopyTrainNumQuery(trainNumQueryList.get(i)));
                                trainNumQueries.add(DeepCopyUtil.deepCopyTrainNumQuery(trainNumQueryList1.get(j)));
                                result.add(trainNumQueries);
                                //System.out.println(result.get(0).get(0).getPrice());
                            }
                        }
                    }
                }
            }
        }
        //System.out.println(result.get(0).get(0).getPrice());
        return result;
    }

    @Transactional
    public List<TrainOrder> buyTransTrainTicket(List<TrainOrder> orderList){
        List<TrainOrder> result = new ArrayList<TrainOrder>();
        result.add(buyTrainTicket(orderList.get(0)));
        result.add(buyTrainTicket(orderList.get(1)));
        return result;


    }
}
